import json
import uuid
import sys

import boto3

from framework.common.json_encoder import PtEncoder
from framework.common.pt_logger import logger
# from core.scheduler.crawler_signal import Signal
from framework.core.crawler_task import Task


class SQSTaskPool(object):
    def __init__(self, name):
        self.client = boto3.client('sqs')
        self.name = name
        self.queue_name = "https://sqs.us-east-2.amazonaws.com/599885534814/%s" % name

    def purge(self):
        response = self.client.purge_queue(
            QueueUrl=self.queue_name
        )
        if response["ResponseMetadata"]["HTTPStatusCode"] == 200:
            logger.info("purge queue success...")
        else:
            logger.warning("purge queue failed...")

    def size(self):
        pass

    def enqueue(self, tasks):
        if tasks is None:
            return
        if isinstance(tasks, list):
            uuids = [str(uuid.uuid1()) for _ in tasks]
            tasks_dict_list = [t.__dict__ for t in tasks]
            entries = list()

            for i, t in enumerate(tasks_dict_list):
                task_obj = {
                    "Id": uuids[i],
                    "MessageBody": json.dumps(tasks_dict_list[i], cls=PtEncoder, indent=4)
                }
                entries.append(task_obj)
            # SQS允许最大10个消息作为entries入队列，这里每10个分一批进行batch enqueue操作
            entries_batches = [entries[x:x + 10] for x in range(0, len(entries), 10)]
            for entries_batch in entries_batches:
                response = self.client.send_message_batch(
                    QueueUrl=self.queue_name,
                    Entries=entries_batch
                )
                if response["ResponseMetadata"]["HTTPStatusCode"] == 200:
                    logger.info("enqueue tasks, %s tasks is queuing to %s" % (len(entries_batch), self.queue_name))
                else:
                    logger.warning("send message failed...")

        # elif isinstance(tasks, Task) or isinstance(tasks, Signal):
        elif isinstance(tasks, Task):
            task_str = json.dumps(tasks, cls=PtEncoder, indent=4)
            response = self.client.send_message(
                QueueUrl=self.queue_name,
                MessageBody=task_str
            )
            if response["ResponseMetadata"]["HTTPStatusCode"] == 200:
                logger.info("enqueue 1 task")
            else:
                logger.warning("send message failed...")
        else:
            raise ValueError("Only support enqueue Task or List<Task>...")
        # for task in tasks:
        #     task_str = json.dumps(task, cls=PtEncoder, indent=4)
        #     response = self.client.send_message(
        #         QueueUrl=self.queue_name,
        #         MessageBody=task_str
        #     )
        #     if response["ResponseMetadata"]["HTTPStatusCode"] != 200:
        #         logger.warning("send message failed...")

    @staticmethod
    def deserialize_task(message_body):
        # message_body = message["messageBodyText"]
        message = json.loads(message_body)
        task = Task.from_json(message)
        return task

    def dequeue(self, keep_in_queue=False):
        logger.debug("trying to dequeue message from queue %s..." % self.queue_name)
        # retry_times = 5
        # retry_times_tmp = retry_times
        response = self.client.receive_message(
            QueueUrl=self.queue_name,
            VisibilityTimeout=10
        )
        if response["ResponseMetadata"]["HTTPStatusCode"] != 200:
            logger.warning("receive message failed...")
            return None
        messages = response.get("Messages")
        logger.debug("message got... %s" % messages)
        if messages is None or len(messages) <= 0:  # queue is empty
            return None
        elif len(messages) > 1:
            logger.warning("only support one task per dequeue...")
            return None
        elif len(messages) == 1:
            try:
                receipt_handle = messages[0]["ReceiptHandle"]
                message_body = messages[0]["Body"]
                # message = json.loads(message_body)
                # task = Task.from_json(message)
                task = self.deserialize_task(message_body)
                if not keep_in_queue:
                    response = self.client.delete_message(
                        QueueUrl=self.queue_name,
                        ReceiptHandle=receipt_handle
                    )
                    if response["ResponseMetadata"]["HTTPStatusCode"] != 200:
                        logger.warning("delete message failed...")
                return task
                # 由source和kind这两个字段判断队列中的是Task还是Signal，
                # 由此有一个问题就是反序列化回来是不知道Signal具体类型的，
                # 因此不能使用TerminatingSignal等具体类型的Signal
                # if message.get("id") and message.get("source") and message.get("kind"):
                #     signal = Signal.from_json(message)
                #     logger.info("received signal %s" % signal)
                #     if signal.kind == "UniCast":
                #         response = self.client.delete_message(
                #             QueueUrl=self.queue_name,
                #             ReceiptHandle=receipt_handle
                #         )
                #         if response["ResponseMetadata"]["HTTPStatusCode"] != 200:
                #             logger.warning("delete message failed...")
                #     return signal
                # else:
                #     task = Task.from_json(message)
                #     response = self.client.delete_message(
                #         QueueUrl=self.queue_name,
                #         ReceiptHandle=receipt_handle
                #     )
                #     if response["ResponseMetadata"]["HTTPStatusCode"] != 200:
                #         logger.warning("delete message failed...")
                #     return task
            except:
                logger.error(sys.exc_info()[0])
                logger.error("receive message from sqs failed, the message will be visible in 10s...")
                return None
